home *** CD-ROM | disk | FTP | other *** search
- /*
- File: Rgn2PlyM.cpp
-
- Contains: Converting a Mac Region into an ODPolygon.
-
- Owned by: Jens Alfke [from an idea by Steve Smith]
-
- Copyright: © 1994 - 1995 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- <2> 2/24/95 jpa Updated for recent API changes.
- <1> 6/15/94 jpa first checked in
- ---------------------------Moved to ODSOM project.
- <1> 5/9/94 jpa first checked in
-
- To Do:
-
- $$$$$ More exception handling
-
- Theory Of Operation:
-
- Apple's patent prevents us from groping the internals of the Region. So we
- draw the region into an offscreen bitmap (banding if necessary, if the whole
- bitmap takes up too much memory) and do a standard image-processing-type scan
- through the bitmap looking for corners. We combine four adjacent pixels into
- a four bit integer and use a switch statement to handle the 16 cases (only 10
- of which represent corners.) Once we know that we can keep track of edges and
- contours, closing or merging contours when they join.
-
- In Progress:
-
- */
-
-
- #ifndef _ALTPOINT_
- #include <AltPoint.h> // Use C++ savvy ODPoint and ODRect
- #endif
-
- #ifndef _ALTPOLY_
- #include <AltPoly.h>
- #endif
-
-
- #ifndef _ODTYPES_
- #include <ODTypes.h>
- #endif
-
- #ifndef _RGN2PLYM_
- #include "Rgn2PlyM.h"
- #endif
-
- #ifndef _PGPOLY_
- #include "PGPoly.h"
- #endif
-
- #ifndef _LIST_
- #include <List.h>
- #endif
-
- #ifndef _ODMEM_
- #include <ODMemory.h>
- #endif
-
- #ifndef _EXCEPT_
- #include <Except.h>
- #endif
-
- #ifndef __TOOLUTILS__
- #include <ToolUtils.h> // $$$$$ Just for cursor stuff
- #endif
-
-
- const long kMaxBitmapSize = 16384; // Max amt of memory to allocate for bitmap
-
-
- #pragma segment ODShape
-
-
- static short
- AllocBitmap( BitMap &bits, const Rect &bounds )
- {
- bits.rowBytes = (((bounds.right-bounds.left)+31) >>5) <<2; // width/8, rounded up to mult of 4
- short height = bounds.bottom-bounds.top;
-
- bits.baseAddr = kODNULL;
- do{
- long size = (long)height * (long)bits.rowBytes;
- if( size <= kMaxBitmapSize ) {
- TRY{
- bits.baseAddr = (Ptr)ODNewPtr(size); // Allocate bitmap
- }CATCH(kODErrOutOfMemory){
- // Ignore out-of-memory but leave baseAddr NULL
- }ENDTRY
- }
-
- if( bits.baseAddr == kODNULL )
- if( height>3 )
- height = (height+3)>>1; // Too big? Halve the number of rows
- else
- THROW(kODErrOutOfMemory); // Can't allocate even 3 rows? Give up
- }while( bits.baseAddr == kODNULL );
-
- // Put the bitmap bounds just above the original bounds. This will force them to be
- // moved (and the bitmap filled) on the first scanline.
- SetRect(&bits.bounds, bounds.left, bounds.top-height,
- bounds.right, bounds.top);
- return height;
- }
-
-
- static short
- SetUpPort( GrafPtr port, const Rect &bounds )
- {
- OpenPort(port);
- short height = AllocBitmap(port->portBits, bounds);
- port->portRect = port->portBits.bounds;
- RectRgn(port->visRgn, &port->portBits.bounds);
- return height;
- }
-
-
- static void
- DisposePort( GrafPtr port )
- {
- ODDisposePtr(port->portBits.baseAddr);
- ClosePort(port);
- }
-
-
- static void
- CloseContour( PGOutputContour *first, PGOutputContour *last, short x, short y,
- PGOutputContour* vert[], const Rect &b, LinkedList &output )
- {
- first->AddVertex(kRight,x,y);
- if( first == last ) {
- // Closing a polygon: add to output list.
- output.AddLast(first);
-
- } else {
- // Merging last with first; find other end of last and change to first.
- first->AppendContour(last,kRight);
-
- for( short i=b.right-b.left-1; i>=0; i-- )
- if( vert[i]==last && b.left+i!=x ) {
- vert[i] = first;
- break;
- }
- WASSERTM(i>=0,"Missing other end of contour!");
- }
- }
-
-
- void
- Rgn2Poly( RgnHandle rgn, ODPolygon &poly )
- {
- Rect b = (**rgn).rgnBBox;
- if( GetHandleSize((Handle)rgn) == sizeof(Region) ) {
- poly.SetRect(b);
- return; // Region is rectangular!
- }
-
- // SetCursor(*GetCursor(plusCursor)); // $$$$$ TEST
-
- b.right++; // Need blank space on right/top/bottom
- b.top--;
- b.bottom++;
-
- // Vert is an array that remembers which polygon corresponds to the vertical edge
- // (if any) currently at x. Correspondingly, curHoriz holds the current horizontal
- // edge at this scanline.
- PGOutputContour *curHoriz = kODNULL;
- PGOutputContour* *vert = new PGOutputContour* [b.right-b.left];
- memset(vert,kODNULL, (b.right-b.left)*sizeof(PGOutputContour*));
-
- PGContourList output;
-
- GrafPtr curPort;
- GetPort(&curPort);
- GrafPort port;
- short portHeight = SetUpPort(&port, b);
-
- long *row0, *row1 = kODNULL; // NULL unnecessary but prevents compiler warnings
-
- for( short y = b.top+1; y<b.bottom; y++ ) {
- if( y>=port.portBits.bounds.bottom ) { // Scroll next band into view
- SetPort(&port);
- SetOrigin(b.left,y-1);
- ClipRect(&port.portRect);
- EraseRect(&port.portRect);
- PaintRgn(rgn);
- SetPort(curPort);
-
- row0 = (long*)(port.portBits.baseAddr);
- } else
- row0 = row1;
- row1 = (long*)( (char*)row0 + port.portBits.rowBytes);
-
- long *srcp0 = row0;
- long *srcp1 = row1;
- long src0, src1;
- short bits = 0;
- char data = 0;
- PGOutputContour* *curVert = &vert[0];
-
- for( short x=b.left; x<b.right; x++ ) { // Scanline:
- // Load in new longwords, or shift the ones we've got:
- if( --bits <=0 ) {
- src0 = *(srcp0++);
- src1 = *(srcp1++);
- bits = 32;
- } else {
- src0 <<= 1;
- src1 <<= 1;
- }
-
- // Shift MSB of each longword into the data, keeping last 4 bits:
- data = (data<<2) & 0x0F;
- if( src0<0 ) // Set data bit if MSB of src0 set
- data |= 2;
- if( src1<0 )
- data |= 1; // Set data bit if MSB of src1 set
-
- //$$$$$ I think this bit-shifting will have to be reworked for little-endian CPUs.
-
- // Call appropriate function depending on state of the latest 4 bits.
- // Bits are stored in 'data' as ABCD, corresponding to pixels-> AC
- // where (x,y) is right in between the four pixels. BD
-
- switch( data ) {
- case 8: // 1000: Lower-right
- case 9: // 1001: Lower-right / Upper-left
- CloseContour(*curVert,curHoriz,x,y, vert,b,output);
- curHoriz = kODNULL;
- *curVert = kODNULL;
-
- if( data==8 )
- break;
- // In case 9, FALL THROUGH:
- case 1: // 0001: Upper-left
- case 14: // 1110: Upper-left negative
- curHoriz = new PGOutputContour(x,y);
- *curVert = curHoriz;
- break;
-
- case 2: // 0010: Lower-left
- curHoriz = *curVert;
- *curVert = kODNULL;
- curHoriz->AddVertex(kLeft,x,y);
- break;
-
- case 4: // 0100: Upper-right
- curHoriz->AddVertex(kRight,x,y);
- *curVert = curHoriz;
- curHoriz = kODNULL;
- break;
-
- case 6: { // 0110: Upper-right / Lower-left
- PGOutputContour *v;
- v = *curVert;
- curHoriz->AddVertex(kRight,x,y);
- *curVert = curHoriz;
-
- curHoriz = v;
- curHoriz->AddVertex(kLeft,x,y);
- break;
- }
- case 7: // 0111: Lower-right negative
- CloseContour(curHoriz,*curVert,x,y, vert,b,output);
- curHoriz = kODNULL;
- *curVert = kODNULL;
- break;
-
- case 11: // 1011: Upper-right negative
- curHoriz->AddVertex(kLeft,x,y);
- *curVert = curHoriz;
- curHoriz = kODNULL;
- break;
-
- case 13: // 1101: Lower-left negative
- curHoriz = *curVert;
- *curVert = kODNULL;
- curHoriz->AddVertex(kRight,x,y);
- break;
-
- case 0: // Rest of cases are no-ops.
- case 3:
- case 5:
- case 10:
- case 12:
- case 15:
- break;
- }
-
- curVert++;
- }
- }
-
- delete vert;
- DisposePort(&port);
-
- output.BuildPolygon(poly);
- output.DeleteAllLinks();
-
- // SetCursor(*GetCursor(crossCursor)); // $$$$$ TEST
- }